<?php

namespace Validator;

use Core\Exception;
/**
 * 验证器类
 * 功能：表单认证，数据库字段认证
 * Class BaseValidator
 * @package Validator
 */
class BaseValidator
{

    /**
     * 要验证的数据
     * @var
     */
    protected $validationData ;

    /**
     * 配置文件
     * @var array
     */
    protected $config = [
        // 是否批量记录（如果有多个错误，就会记录到数组一起返回，fase:碰到错误就返回）
        'isBatch' => false,
        // 错误返回，true:抛出异常，false:return
        'catchException' => true
    ];

    /**
     * 字段中文映射表
     * @var array
     */
    protected $fields =[
        'id' => '编号',
        'name' => '姓名',
        'amount' => 'amount',
    ];

    /**
     * 错误信息模板
     * @var array
     */
    protected $ruleMsgTemplate =[
        'require'   => '10102',
        'gt'        => '10100:{field}格式非法',
        'isNumber'  => '10100:{field}必须是数字',
        'isMoney'   => '10100:{field}金额格式有误',
        'isPhone'   => '10100:手机号码格式有误',
        'isMobile'  => '10100:请输入正确手机号',
        'isEmail'   => '10100:邮箱格式错误',
        'length'    => '10100:长度格式要求{field}位',
    ];


    public function setConfig($data)
    {
        $this->config = array_merge($this->config, $data);
        return $this;
    }

    /**
     * 单个字段验证
     * @param $data
     * @param $rules
     */
    public  function singleValidate($data, $rules)
    {

    }
    /**
     * 数值验证
     * @param array $data
     * @param array $rules
     * @return array|bool|mixed
     * @throws \Core\Exception
     */
    public function validate($data = [], $rules = [])
    {
        $errMsg = [];
        $this->validationData = $data;
        // 二维数组验证
        foreach ($rules as $key => $rule)
        {
            // 多个规则隔开，rule1|rule2:
            if (strpos($key, '|')) {
                list($key, $title) = explode('|', $key);
            }else{
                $title = $key;
            }
            $value = $this->getDataValue($data, $key);
            // 逐个字段检验
            $checkRet= $this->checkItem($key, $value, $rule, $data, $title);
            if( true !== $checkRet){
                if($this->config['isBatch']){
                    // 批量验证
                    if (is_array($checkRet)) {
                        $errMsg = array_merge($errMsg, $checkRet);
                    } else {
                        $errMsg[$key] = $checkRet;
                    }
                }else{
                    if($this->config['catchException']){
                        throw new Exception((int)$checkRet['code'], $checkRet['msg']);
                    }else{
                        return $checkRet;
                    }

                }
            }
        }
        return empty($errMsg) ? ($this->config['catchException'] ? true : ['code'=>1,'msg'=>'']) : $errMsg;
    }


    /**
     * 验证单个字段规则
     * @access protected
     * @param  string    $field  字段名
     * @param  mixed     $value  字段值
     * @param  mixed     $rules  验证规则
     * @param  array     $data  数据
     * @param  string    $title  字段描述
     * @param  array     $msg  提示信息
     * @return mixed
     */
    protected function checkItem($field, $value, $rules, $data, $title = '', $msg = [])
    {
        if(empty($field) OR empty($rules) ){
            return true;
        }
        // 多规则支持 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
        if (is_string($rules)) {
            $rules = explode('|', $rules);
        }
        $result = true;
        $errMsg = [];
        foreach ($rules as $key => $rule) {
            $condition = '';
            if (strpos($rule, ':')) {
                list($rule, $condition) = explode(':', $rule, 2);
            }
            $result = call_user_func_array([$this, $rule], [$value, $condition, $field]);
            if( true !== $result){
                if($this->config['isBatch']){
                    $errMsg = $this->getMsg($rule, $field);
                    array_push($errMsg, $result);
                }else{
                    return $this->getMsg($rule, $field);
                }
            }
        }
        return empty($errMsg) ? true : $errMsg;
    }

    /**
     * 获取数据值
     * @access protected
     * @param  array     $data  数据
     * @param  string    $key  数据标识 支持多维
     * @return mixed
     */
    protected function getDataValue($data, $key)
    {
        if (is_numeric($key)) {
            $value = $key;
        } elseif (strpos($key, '.')) {
            // 支持多维数组验证
            foreach (explode('.', $key) as $key) {
                if (!isset($data[$key])) {
                    $value = null;
                    break;
                }
                $value = $data = $data[$key];
            }
        } else {
            $value = isset($data[$key]) ? $data[$key] : null;
        }
        return $value;
    }

    protected function getMsg($rule, $field)
    {
        if(!isset($this->ruleMsgTemplate[$rule])){
            $field = '';
        }
        $fieldName = $this->fields[$field];
        $template = $this->ruleMsgTemplate[$rule];
        if (strpos($template, ':')) {
            // code:msg 这种有自定义错误信息的
            list($code, $template) = explode(':', $template, 2);
            $msg = str_replace('{field}', $fieldName, $template);
            return compact('code', 'msg');
        }else{
            // 只定义了code没msg,在errorAction和错误表做联动
            return ['code' => $template, 'msg' => ''];
        }
    }

    /**
     * 规则：参数必填
     * @param $value
     * @param string $condition
     * @return bool
     */
    protected function require($value, $condition = '')
    {
        return empty($value) ? false : true;
    }

    /**
     * 规则：必须是数字
     * @param $value
     * @param string $condition
     * @return bool
     */
    protected function isNumber($value, $condition = '')
    {
        return is_numeric($value) ?  true : false;
    }

    /**
     * 规则：金额格式校验
     * @param $value
     * @param string $condition
     * @return bool
     */
    protected function isMoney($value, $condition = '')
    {
        $money_reg = '/^[0-9]+(.[0-9]{1,2})?$/';
        if (preg_match($money_reg, $value)) {
            return true;
        }else{
            return false;
        }
    }
    /**
     * 规则：手机号码格式校验
     * @param $value
     * @param string $condition
     * @return string
     */
    protected function isPhone($value, $condition = '')
    {
        if (empty($value)){
            return false;
        }
        $phone_reg = '/^((\(d{2,3}\))|(\d{3}\-))?1(3|4|5|6|7|8|9)\d{9}$/';
        if (preg_match($phone_reg, $value)) {
            return true;
        }else{
            return false;
        }
    }


    /**
     * 规则：email格式验证
     * @param $value
     * @param string $condition
     * @return bool
     */
    protected function isEmail($value, $condition = '')
    {
        if (empty($value)){
            return false;
        }
        $reg = '/^[_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,4}$/i';
        if (preg_match($reg, $value)) {
            return true;
        }else{
            return false;
        }
    }


    /**
     * 规则:长度验证
     * @param $value
     * @param string $condition
     * @return bool
     */
    protected function length($value, $condition = '', $field)
    {
        if (empty($value) OR empty($condition)){
            return false;
        }
        $condition = explode(',', $condition);
        list($short_num, $longer_num) = [trim($condition[0]), trim($condition[1])];

        if (count($condition) == 1){
            //按指定长度校验
            if (strlen($value) == $short_num){
                return true;
            }else{
                $this->fields[$field] = $short_num;
                return false;
            }
        }else{
            //按长度范围校验
            if (strlen($value) < $short_num || strlen($value) > $longer_num){
                $this->fields[$field] = $short_num.'~'.$longer_num;
                return false;
            }else{
                return true;
            }
        }

    }
}